package cn.siat.gene.service.impl;


import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.siat.common.core.exception.ServiceException;
import cn.siat.common.core.utils.StringUtils;
import cn.siat.common.core.web.page.TableDataInfo;
import cn.siat.common.redis.service.RedisService;
import cn.siat.common.security.utils.SecurityUtils;
import cn.siat.gene.service.ComponentService;
import cn.siat.gene.domain.DesignTBExternalPaccout;
import cn.siat.gene.mapper.DesignTBExternalPaccoutMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 元件库外部接口Service实现类
 */
@Service
public class ComponentServiceImpl implements ComponentService {

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    RedisService redisService;

    @Autowired
    private DesignTBExternalPaccoutMapper designTBExternalPaccoutMapper;

    @Value("${component.baseUrl}")
    private String baseUrl;
    @Value("${component.email}")
    private String adminEmail;
    @Value("${component.password}")
    private String adminPassword;
    @Value("${component.folderId}")
    private String folderId;

//    @Value("${sequence.url}")
//    private String sequenceUrl;

    private String authorizationAdmin;

    /**
     * 创建消息头
     * @param isJson
     * @return
     */
    public HttpHeaders createHeaders(String authorization,Boolean isJson) {
        return new HttpHeaders() {
            {
                set("authorization", authorization);
                if(isJson){
                    set("Accept", "application/json");
                    set("Content-Type", "application/json");
                }
            }
        };
    }

//    /**
//     * 通过python接口读取genbank
//     * @param url
//     * @param partId
//     * @return
//     */
//    public JSONObject getSequence(String url,String partId){
//        Map<String,String> param = new HashMap<>();
//        param.put("url",url);
//        param.put("partId",partId);
//        return restTemplate.postForObject(sequenceUrl, param,JSONObject.class);
//    }

    /**
     * 从关联账户表获取账号密码
     */
    private DesignTBExternalPaccout getExternalAccount(){
        QueryWrapper<DesignTBExternalPaccout> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_bind", 1)
                .eq("is_delete", 1)
                .eq("user_id", SecurityUtils.getUserId())
                .eq("external_system_name", "元件库系统");
        DesignTBExternalPaccout designTBExternalPaccout = designTBExternalPaccoutMapper.selectOne(queryWrapper);
        if(designTBExternalPaccout == null){
            throw new ServiceException("当前用户未绑定元件库账户");
        }
        return designTBExternalPaccout;
    }

    /**
     * 元件库通用
     * @param api
     * @param method 请求方式(HttpMethod)
     * @param params post请求携带参数
     * @param flag 0为默认账号，1为个人绑定账号
     * @return
     */
    public JSONObject getResult(String api,HttpMethod method,Map params,Integer flag){
        String authorization = "";
        //默认列表使用管理员账户查询
        if( flag==0){
            if(authorizationAdmin == null){
                Object componentTokenAdmin = redisService.getCacheObject("Component_Token_Admin:Gene");
                //如果令牌为空，从redis缓存获取令牌
                if(componentTokenAdmin == null || "".equals(componentTokenAdmin)){
                    getAdminToken();
                }else{
                    this.authorizationAdmin= ((Map<String, String>) componentTokenAdmin).get("authorization");
                }
            }
            authorization = this.authorizationAdmin;
        }else{
            Object componentToken = redisService.getCacheObject("Component_Token:" + SecurityUtils.getUserId());
            if(componentToken == null || "".equals(componentToken) ){
                authorization = getToken();
            }else {
                authorization= ((Map<String, String>) componentToken).get("authorization");
            }
        }

        //使用restTemplate发送请求
        String url = baseUrl+api;
        HttpEntity<Map> httpEntity = new HttpEntity<>(params, createHeaders(authorization,method.equals(HttpMethod.POST)));

        JSONObject body = null;
        try{
            body = restTemplate.exchange(url, method, httpEntity, JSONObject.class).getBody();
        }catch (Exception e){
            String message = e.getMessage();
            String code = message.substring(0,message.indexOf(":"));
            String resultBody = message.substring(message.indexOf(":")+1).trim();
            resultBody = resultBody.substring(1,resultBody.length()-1);
            System.err.println(resultBody);
            throw new ServiceException("【元件库系统异常】:获取数据失败! ");
        }
        if (body == null) {
            throw new ServiceException("获取数据失败!");
        }
        //登录超时重新获取令牌
        if(body.getInteger("code") == HttpStatus.HTTP_UNAUTHORIZED || body.getString("message").contains("请重新登录")){
            if(api.startsWith("folder/getComponentListByFolder")){
                getAdminToken();
                authorization = this.authorizationAdmin;
            }else{
                authorization = getToken();
            }
            //重新发送请求
            httpEntity = new HttpEntity<>(params, createHeaders(authorization,method.equals(HttpMethod.POST)));

            try{
                body = restTemplate.exchange(url, method, httpEntity, JSONObject.class).getBody();
            }catch (Exception e){
                String message = e.getMessage();
                throw new ServiceException(message);
            }
        }
        return body;
    }

    /**
     * 获取管理员token
     */
    private void getAdminToken(){
        String api = "user/getToken";
        String url = baseUrl+api;
        //获取管理员token
        Map<String ,String> admin = new HashMap<>();
        admin.put("email",adminEmail);
        admin.put("password",adminPassword);
        JSONObject body = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(admin, null), JSONObject.class).getBody();
        Boolean success = body.getBoolean("success");
        if(success){
            Map<String, Object> bodyMap = (Map<String, Object>) body.get("body");
            String verifyKey = "Component_Token_Admin:Gene";
            redisService.setCacheObject(verifyKey,bodyMap, 60*60*20L, TimeUnit.SECONDS );
            this.authorizationAdmin = (String) bodyMap.get("authorization");
        }else{
            throw new ServiceException("获取元件库管理员Token失败!  "+body.getString("message"));
        }
    }

    /**
     * 从元件库获取Token
     * @return
     */
    @Override
    public String getToken() {
        String api = "user/getToken";
        String url = baseUrl+api;
        //获取普通用户token
        DesignTBExternalPaccout account = getExternalAccount();
        String email = account.getExternalSystemUserName();
        String password = account.getExternalSystemPassword();
        Map<String ,String> general = new HashMap<>();
        general.put("email",email);
        general.put("password",password);
        JSONObject body = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(general, null), JSONObject.class).getBody();
        Boolean success = body.getBoolean("success");
        if(success){
            Map<String, Object> bodyMap = (Map<String, Object>) body.get("body");
            String verifyKey = "Component_Token:" + SecurityUtils.getUserId();
            redisService.setCacheObject(verifyKey,bodyMap, 60*60*20L, TimeUnit.SECONDS );
        }else{
            throw new ServiceException("获取元件库Token失败!  "+body.getString("message"));
        }
        return body.getJSONObject("body").getString("authorization");
    }

    /**
     * 验证元件库账号密码
     * @return
     */
    @Override
    public JSONObject verifyToken(String username, String password) {
        String api = "user/getToken";
        String url = baseUrl+api;
        Map<String ,String> general = new HashMap<>();
        general.put("email",username);
        general.put("password",password);
        JSONObject body = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(general, null), JSONObject.class).getBody();
        if(body.getBoolean("success")){
            String verifyKey = "Component_Token:" + SecurityUtils.getUserId();
            redisService.deleteObject(verifyKey);
        }
        return body;
    }





    //获取默认列表
    @Override
    public TableDataInfo getList(String input, String type, Integer pageNum, Integer pageSize) {
        List<JSONObject> list = null;
        TableDataInfo tableDataInfo = new TableDataInfo();
        try {
            String searchContent = "";
            if(StringUtils.isNotEmpty(input) && StringUtils.isNotEmpty(type)){
//                input = dealString(input);
                searchContent = "&"+type+"="+input;
            }
            String api = "folder/getComponentListByFolder?" +
                    "showAllDetails=false" +
                    "&page="+pageNum+
                    "&limit="+pageSize+
                    "&folderId="+folderId+
                    "&sequenceFlag=1"+
                    "&types=parts,plasmid,strain"+
                    searchContent;
            JSONObject result = getResult(api, HttpMethod.GET, null,0);
//            System.err.println(result);
            JSONObject body = null;
            if(result.getInteger("code") == HttpStatus.HTTP_OK){
                body = result.getJSONObject("body");
                List<JSONObject> arr = body.getJSONArray("records").toJavaList(JSONObject.class);
                list = getSimpleList(arr);
            }else{
                throw new ServiceException("【元件库系统 返回提示】"+result.getString("message"));
            }
            tableDataInfo.setRows(list);
//            Long total = getDefaultListTotal();
            tableDataInfo.setTotal(body.getLong("total"));
        }catch (Exception e){
            System.err.println("抛了");
            throw e;
        }
        return tableDataInfo;
    }


    /**
     * 获取默认元件库列表数据量
     * @return
     */
    public Long getDefaultListTotal(){
        String api = "folder/getFolderById?folderId="+folderId;
        JSONObject result = getResult(api, HttpMethod.GET, null,0);
        if(result.getInteger("code") == HttpStatus.HTTP_OK){
            String  totalComponent= result.getJSONObject("body").getString("totalComponent");
            return Long.valueOf(totalComponent);
        }else{
            throw new ServiceException(result.getString("message"));
        }
    }

    //获取更多列表
    @Override
    public TableDataInfo getMoreList(String input, String type,  Integer pageNum, Integer pageSize) {
        TableDataInfo tableDataInfo = null;
        //判断当前是公共列表还是查询列表
        try {
            if ("name".equals(type)) {
                tableDataInfo = getComponentListByName(input,pageNum,pageSize);
            } else {
                List<JSONObject> list = getComponentListByCode(input);
                tableDataInfo = new TableDataInfo();
                tableDataInfo.setTotal(list.size());
                //对list分页
                list = list.stream().skip((pageNum-1)*pageSize).limit(pageSize).
                        collect(Collectors.toList());
                tableDataInfo.setRows(list);
            }
        }catch (Exception e){
            System.err.println("抛了");
            throw e;
        }
        return tableDataInfo;
    }

    /**
     * 根据元件名称获取元件基础信息
     * @param name
     * @return
     */
    public TableDataInfo getComponentListByName(String name,Integer pageNum, Integer pageSize){
//        name = dealString(name);
        String api = "component/getComponentListByName?" +
                "name="+name+
                "&page="+pageNum+
                "&limit="+pageSize+
                "&sequenceFlag=1"+
                "&types=parts,plasmid,strain";
        JSONObject result = getResult(api, HttpMethod.GET, null,1);
        JSONObject body = null;
        if(result.getInteger("code") == HttpStatus.HTTP_OK){
            body = result.getJSONObject("body");
            List<JSONObject> list = body.getJSONArray("records").toJavaList(JSONObject.class);
            return new TableDataInfo(getSimpleList(list),body.getInteger("total"));
        }else{
            throw new ServiceException("获取元件信息失败!");
        }
    }

    /**
     * 根据元件code获取元件列表
     * @param codes
     * @return
     */
    public List<JSONObject> getComponentListByCode(String codes) {
        codes = codes.replace("，", ",").replace(" ", "");
        String[] codeArr = codes.split(",");
        //筛选掉蛋白和种子
        codeArr = Arrays.stream(codeArr).filter(a -> {
            return !(a.startsWith("protein")||a.startsWith("seed"));
        }).toArray(String[]::new);
        List<JSONObject> resultList = new ArrayList<>();
        for (String code : codeArr) {
            String api = "component/getComponentByCode?code="+code;
            JSONObject result = getResult(api, HttpMethod.GET, null,1);
            if(result.getBoolean("success")&& result.getJSONObject("body").getInteger("sequenceFlag")==1){
                resultList.add(result.getJSONObject("body"));
            }
        }

        List<JSONObject> result = new ArrayList<>();
        for (JSONObject jsonObject : resultList) {
            JSONObject oveSequence = jsonObject.getJSONObject("oveSequence");
            JSONObject json = new JSONObject();
            json.put("code", jsonObject.getString("code"));
            json.put("name", jsonObject.getString("name"));
            String type = jsonObject.getString("type");
            json.put("type", type);
            if(oveSequence == null){
                json.put("length", null);
                JSONObject fieldDetails = jsonObject.getJSONObject("fieldDetails");
                String typeStr = "";
                if("parts".equals(type)){
                    typeStr = "部件信息";
                }else if("strain".equals(type)){
                    typeStr = "菌株信息";
                }else if("plasmid".equals(type)){
                    typeStr = "质粒信息";
                }else if("protein".equals(type)){
                    typeStr = "蛋白信息";
                }else if("seed".equals(type)){
                    typeStr = "种子信息";
                }
                Object o = fieldDetails.get(typeStr);
                if(fieldDetails !=null && o!=null){
                    if(o instanceof List){
                        json.put("description", fieldDetails.getJSONArray(typeStr).getJSONObject(0).getString("描述"));
                    }else {
                        json.put("description", fieldDetails.getJSONObject(typeStr).getString("描述"));
                    }
                }else{
                    json.put("description", null);
                }
            }else{
                json.put("length", oveSequence.getString("length"));
                json.put("description", oveSequence.getString("description"));
            }
            result.add(json);
        }
        return result;
    }

    /**
     * 只返回元件库列表所需字段
     * @param list
     * @return
     */
    public List<JSONObject> getSimpleList(List<JSONObject> list){
        List<JSONObject> result = new ArrayList<>();
        for (JSONObject jsonObject : list) {
            String sequenceSize = jsonObject.getString("sequenceSize");
//            if(jsonObject.getInteger("sequenceFlag") == 1 && (!"0".equals(sequenceSize)) && (!"protein".equals(jsonObject.getString("type")))){
            JSONObject json = new JSONObject();
            json.put("code", jsonObject.getString("code"));
            json.put("name", jsonObject.getString("name"));
            json.put("length", sequenceSize);
            json.put("description", jsonObject.getString("componentDescribe"));
            json.put("type", jsonObject.getString("type"));
            result.add(json);
//            }
        }
        return result;
    }

    /**
     * 根据元件ID获取元件详情
     * @param code
     * @return
     */
    @Override
    public JSONObject getComponentByCode(String code,Integer flag) {
        String api = "component/getComponentByCode?code="+code;
        JSONObject result = getResult(api, HttpMethod.GET, null,flag);
        JSONObject partInfo = new JSONObject();
        if(result.getBoolean("success")){
            JSONObject body = result.getJSONObject("body");
            if(body.getInteger("sequenceFlag")==0 || body.getJSONObject("oveSequence") == null){
                //查询成功但是没有序列
                throw new ServiceException("该元件没有序列");
            }else{
                partInfo.put("partName", body.getString("name"));
                String partType = body.getString("type");
                partInfo.put("partType", partType);
                JSONObject fieldDetails = body.getJSONObject("fieldDetails");
                String subType = null;
                String description = null;
                partInfo.put("bpLength",body.getJSONObject("oveSequence").getString("length"));
                partInfo.put("content",body.getJSONObject("oveSequence").getString("sequence"));
                partInfo.put("circular",body.getJSONObject("oveSequence").getBoolean("circular"));
                partInfo.put("sourceName",body.getJSONObject("oveSequence").getString("name"));
                partInfo.put("code",code);
                partInfo.put("format","GENBANK");
            }
        }else{
            throw new ServiceException("接口查询失败");
        }
        return partInfo;
    }

}
